Kattava opas Cross-Origin Resource Sharing (CORS) -mekanismiin, joka kattaa konfiguraation, tietoturvavaikutukset ja parhaat käytännöt kehittäjille.
Cross-Origin Resource Sharing (CORS): Konfiguraatio ja tietoturvan parhaat käytännöt
Web-kehityksen maailmassa tietoturva on ensisijaisen tärkeää. Yksi kriittinen verkkoturvallisuuden osa-alue on hallita, miten yhdestä alkuperästä (origin) peräisin olevat verkkosivut voivat käyttää resursseja toisesta alkuperästä. Tässä kohtaa Cross-Origin Resource Sharing (CORS) astuu kuvaan. CORS on selaimen tietoturvaominaisuus, joka rajoittaa verkkosivuja tekemästä pyyntöjä eri verkkotunnukseen kuin se, josta sivu on ladattu. Tämä mekanismi on olemassa estääkseen haitallisia verkkosivustoja pääsemästä käsiksi arkaluontoisiin tietoihin. Tämä artikkeli tarjoaa kattavan oppaan CORS-mekanismiin, käsitellen sen konfiguraatiota, tietoturvavaikutuksia ja parhaita käytäntöjä.
Samaa alkuperää koskevan käytännön (Same-Origin Policy) ymmärtäminen
CORS rakentuu samaa alkuperää koskevan käytännön (Same-Origin Policy) perustalle, joka on verkkoselainten toteuttama perustavanlaatuinen tietoturvamekanismi. Samaa alkuperää koskeva käytäntö rajoittaa verkkosivuja tekemästä pyyntöjä eri verkkotunnukseen kuin se, josta sivu on ladattu. Kahden URL-osoitteen katsotaan olevan samaa alkuperää, jos niillä on sama protokolla (esim. HTTP tai HTTPS), isäntä (esim. example.com) ja portti (esim. 80 tai 443). Esimerkiksi:
http://example.comjahttp://example.com/pathovat samaa alkuperää.http://example.comjahttps://example.comovat eri alkuperää (eri protokollat).http://example.comjahttp://www.example.comovat eri alkuperää (eri isännät).http://example.com:80jahttp://example.com:8080ovat eri alkuperää (eri portit).
Samaa alkuperää koskeva käytäntö on suunniteltu estämään sivustojenvälisiä komentosarjahyökkäyksiä (Cross-Site Scripting, XSS), joissa haitallinen verkkosivusto syöttää komentosarjoja luotettuun verkkosivustoon käyttäjätietojen varastamiseksi tai luvattomien toimintojen suorittamiseksi. Ilman samaa alkuperää koskevaa käytäntöä haitallinen verkkosivusto voisi mahdollisesti päästä käsiksi pankkitilisi tietoihin, jos olisit kirjautuneena verkkopankkiisi toisella välilehdellä.
Mitä on Cross-Origin Resource Sharing (CORS)?
Vaikka samaa alkuperää koskeva käytäntö on ratkaisevan tärkeä turvallisuuden kannalta, se voi olla myös rajoittava laillisissa tilanteissa, joissa verkkosivustojen on käytettävä resursseja eri alkuperistä. Esimerkiksi osoitteessa example.com isännöity verkkosovellus saattaa joutua hakemaan tietoja API-rajapinnasta, joka sijaitsee osoitteessa api.example.net. CORS tarjoaa mekanismin samaa alkuperää koskevan käytännön kiertämiseksi hallitusti, sallien verkkosivujen tehdä alkuperien välisiä pyyntöjä, kun palvelin on nimenomaisesti antanut siihen luvan.
CORS toimii lisäämällä HTTP-otsakkeita palvelimen vastaukseen, jotka ilmaisevat, mitkä alkuperät saavat käyttää resurssia. Selain tarkistaa sitten nämä otsakkeet ja estää pyynnön, jos pyynnön tekevän verkkosivun alkuperä ei ole sallittu.
Kuinka CORS toimii: HTTP-otsakkeet
CORS perustuu tiettyihin HTTP-otsakkeisiin alkuperien välisten pyyntöjen mahdollistamiseksi. Tässä ovat keskeiset asiaan liittyvät otsakkeet:
1. Origin (Pyyntöotsake)
Selain lähettää Origin-otsakkeen alkuperien välisissä pyynnöissä. Se ilmaisee pyynnön tekevän verkkosivun alkuperän (protokolla, isäntä ja portti). Esimerkiksi:
Origin: http://example.com
2. Access-Control-Allow-Origin (Vastausotsake)
Access-Control-Allow-Origin-otsake on tärkein CORS-otsake. Se määrittelee, mitkä alkuperät saavat käyttää resurssia. Sillä voi olla yksi seuraavista arvoista:
- Tietty alkuperä: Esimerkiksi
Access-Control-Allow-Origin: http://example.comsallii pyynnöt vain osoitteestahttp://example.com. *(jokerimerkki):Access-Control-Allow-Origin: *sallii pyynnöt mistä tahansa alkuperästä. Tätä tulee käyttää varoen, koska se käytännössä poistaa samaa alkuperää koskevan käytännön käytöstä kyseiseltä resurssilta.
Esimerkki:
Access-Control-Allow-Origin: https://www.example.com
3. Access-Control-Allow-Methods (Vastausotsake)
Access-Control-Allow-Methods-otsake määrittelee HTTP-metodit (esim. GET, POST, PUT, DELETE), jotka ovat sallittuja alkuperien välisessä pyynnössä. Tämä vaaditaan esikyselyille (selitetty alla).
Esimerkki:
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
4. Access-Control-Allow-Headers (Vastausotsake)
Access-Control-Allow-Headers-otsake määrittelee HTTP-otsakkeet, jotka ovat sallittuja alkuperien välisessä pyynnössä. Tämä vaaditaan myös esikyselyille.
Esimerkki:
Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With
5. Access-Control-Allow-Credentials (Vastausotsake)
Access-Control-Allow-Credentials-otsake määrittelee, tuleeko selaimen sisällyttää tunnistetietoja (esim. evästeitä, valtuutusotsakkeita) alkuperien väliseen pyyntöön. Sillä voi olla kaksi arvoa: true tai false. Jos arvoksi on asetettu true, Access-Control-Allow-Origin-otsaketta ei voi asettaa arvoon *. Sen on oltava tietty alkuperä.
Esimerkki:
Access-Control-Allow-Credentials: true
6. Access-Control-Max-Age (Vastausotsake)
Access-Control-Max-Age-otsake määrittelee, kuinka monta sekuntia selain voi tallentaa esikyselyn tulokset välimuistiin. Tämä voi parantaa suorituskykyä vähentämällä esikyselyjen määrää.
Esimerkki:
Access-Control-Max-Age: 3600
Yksinkertaiset pyynnöt vs. esikyselyt
CORS erottaa kahden tyyppisiä alkuperien välisiä pyyntöjä: yksinkertaiset pyynnöt ja esikyselyt.
Yksinkertaiset pyynnöt
Yksinkertainen pyyntö on pyyntö, joka täyttää seuraavat kriteerit:
- Metodi on
GET,HEADtaiPOST. - Jos metodi on
POST,Content-Type-otsake on yksi seuraavista:application/x-www-form-urlencoded,multipart/form-datataitext/plain. - Pyyntö ei aseta mukautettuja otsakkeita (muita kuin selaimen automaattisesti asettamia).
Yksinkertaisten pyyntöjen tapauksessa selain lähettää pyynnön suoraan palvelimelle. Palvelin vastaa sitten asianmukaisilla CORS-otsakkeilla. Jos alkuperä on sallittu, selain käsittelee vastauksen. Muussa tapauksessa selain estää vastauksen ja antaa virheilmoituksen.
Esikyselyt
Selain lähettää esikyselyn (preflight request) ennen varsinaisen alkuperien välisen pyynnön tekemistä, jos pyyntö ei täytä yksinkertaisen pyynnön kriteerejä. Tämä tapahtuu tyypillisesti, kun pyyntö käyttää muuta metodia kuin GET, HEAD tai POST, tai kun pyyntö asettaa mukautettuja otsakkeita.
Esikysely on OPTIONS-pyyntö, joka sisältää seuraavat otsakkeet:
Origin: Pyynnön tekevän verkkosivun alkuperä.Access-Control-Request-Method: HTTP-metodi, jota käytetään varsinaisessa pyynnössä.Access-Control-Request-Headers: Pilkulla erotettu luettelo mukautetuista otsakkeista, joita käytetään varsinaisessa pyynnössä.
Palvelin vastaa sitten seuraavilla otsakkeilla:
Access-Control-Allow-Origin: Alkuperä, jolla on oikeus käyttää resurssia.Access-Control-Allow-Methods: HTTP-metodit, jotka ovat sallittuja alkuperien välisessä pyynnössä.Access-Control-Allow-Headers: HTTP-otsakkeet, jotka ovat sallittuja alkuperien välisessä pyynnössä.Access-Control-Max-Age: Sekuntien määrä, jonka selain voi tallentaa esikyselyn tulokset välimuistiin.
Jos palvelin vastaa asianmukaisilla CORS-otsakkeilla, selain jatkaa varsinaisen alkuperien välisen pyynnön tekemistä. Muussa tapauksessa selain estää pyynnön ja antaa virheilmoituksen.
CORS-konfiguraatioesimerkkejä
CORS:n toteutus vaihtelee käyttämäsi palvelinteknologian mukaan. Tässä on joitain esimerkkejä yleisille palvelinpuolen kielille ja kehyksille:
Node.js ja Express
cors-middlewaren käyttö on yleinen tapa konfiguroida CORS Node.js:ssä Expressin kanssa:
const express = require('express');
const cors = require('cors');
const app = express();
// Enable CORS for all origins
app.use(cors());
// Enable CORS for a specific origin
// app.use(cors({ origin: 'http://example.com' }));
// Enable CORS with options
// app.use(cors({
// origin: ['http://example.com', 'http://localhost:3000'],
// methods: ['GET', 'POST', 'PUT', 'DELETE'],
// allowedHeaders: ['Content-Type', 'Authorization'],
// credentials: true
// }));
app.get('/api/data', (req, res) => {
res.json({ message: 'Hello from the API!' });
});
app.listen(3001, () => {
console.log('Server listening on port 3001');
});
Python ja Flask
Voit käyttää Flask-CORS-laajennusta CORS:n konfigurointiin Flaskissa:
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
# Enable CORS for all origins
CORS(app)
# Enable CORS for specific origins
# CORS(app, origins=['http://example.com', 'http://localhost:3000'])
@app.route('/api/data')
def get_data():
return {'message': 'Hello from the API!'}
if __name__ == '__main__':
app.run(port=3001)
Java ja Spring Boot
Spring Boot tarjoaa useita tapoja konfiguroida CORS. Yksi tapa on käyttää @CrossOrigin-annotaatiota:
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@CrossOrigin(origins = "http://example.com") // Specific origin
public class ApiController {
@GetMapping("/api/data")
public String getData() {
return "Hello from the API!";
}
}
// Global CORS configuration (using WebMvcConfigurer):
// @Configuration
// public class CorsConfig implements WebMvcConfigurer {
// @Override
// public void addCorsMappings(CorsRegistry registry) {
// registry.addMapping("/**")
// .allowedOrigins("http://example.com", "http://localhost:3000")
// .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
// .allowedHeaders("Content-Type", "Authorization")
// .allowCredentials(true)
// .maxAge(3600);
// }
// }
PHP
PHP:ssä voit asettaa CORS-otsakkeet suoraan skriptissäsi:
<?php
header("Access-Control-Allow-Origin: http://example.com");
header("Content-Type: application/json");
$data = array("message" => "Hello from the API!");
echo json_encode($data);
?>
CORS-tietoturvanäkökohdat
Vaikka CORS mahdollistaa alkuperien väliset pyynnöt, on ratkaisevan tärkeää ymmärtää tietoturvavaikutukset ja toteuttaa se oikein haavoittuvuuksien välttämiseksi.
1. Vältä Access-Control-Allow-Origin: * -määrityksen käyttöä tuotannossa
Jokerimerkin * käyttö Access-Control-Allow-Origin-otsakkeessa sallii pyynnöt mistä tahansa alkuperästä, mikä käytännössä poistaa samaa alkuperää koskevan käytännön käytöstä kyseiseltä resurssilta. Tämä voi altistaa API-rajapintasi haitallisille verkkosivustoille, jotka voivat mahdollisesti varastaa käyttäjätietoja tai suorittaa luvattomia toimintoja. Määritä sen sijaan tarkasti ne alkuperät, joilla on lupa käyttää resurssia. Jos esimerkiksi verkkosovelluksesi sijaitsee osoitteessa example.com ja sen on käytettävä API-rajapintaa osoitteessa api.example.com, aseta otsakkeeksi Access-Control-Allow-Origin: http://example.com.
Yleinen esimerkki: Kuvittele rahoituspalvelun API, joka asettaa Access-Control-Allow-Origin: *. Haitallinen verkkosivusto voisi tällöin tehdä pyyntöjä tähän API:in kirjautuneen käyttäjän puolesta ja mahdollisesti siirtää varoja ilman käyttäjän tietämystä.
2. Vahvista Origin-otsake palvelimella
Vaikka määrittäisit luettelon sallituista alkuperistä, on tärkeää vahvistaa Origin-otsake palvelimella, jotta hyökkääjät eivät voi väärentää alkuperää. Hyökkääjä voisi mahdollisesti lähettää pyynnön väärennetyllä Origin-otsakkeella kiertääkseen CORS-tarkistukset. Tämän lieventämiseksi vertaa Origin-otsaketta luotettujen alkuperien luetteloon palvelinpuolella. Jos alkuperä ei ole luettelossa, hylkää pyyntö.
Yleinen esimerkki: Harkitse verkkokauppa-alustaa. Hyökkääjä voisi yrittää jäljitellä laillisen kaupan Origin-tietoa päästäkseen käsiksi arkaluontoisiin asiakastietoihin verkkokaupan API-rajapinnan kautta.
3. Ole varovainen Access-Control-Allow-Credentials: true -asetuksen kanssa
Jos asetat Access-Control-Allow-Credentials: true, Access-Control-Allow-Origin-otsaketta ei voi asettaa arvoon *. Sen on oltava tietty alkuperä. Tämä johtuu siitä, että tunnistetietojen salliminen mistä tahansa alkuperästä voi luoda tietoturvariskin, koska se voisi antaa haitallisille verkkosivustoille pääsyn käyttäjätietoihin, jos ne onnistuvat huijaamaan käyttäjän vierailemaan sivustollaan samalla kun käyttäjä on kirjautuneena kohdesivustolle. Tämä asetus on tärkeä käsiteltäessä evästeitä tai valtuutusotsakkeita.
Yleinen esimerkki: Sosiaalisen median alusta, joka sallii alkuperien väliset pyynnöt tunnistetiedoilla, vaatii huolellista hallintaa estääkseen luvattoman pääsyn käyttäjätileille.
4. Määritä Access-Control-Allow-Methods ja Access-Control-Allow-Headers oikein
Salli vain ne HTTP-metodit ja -otsakkeet, jotka ovat välttämättömiä alkuperien välisille pyynnöille. Jos sinun tarvitsee sallia vain GET- ja POST-pyynnöt, älä salli PUT-, DELETE- tai muita metodeja. Salli vastaavasti vain ne tietyt otsakkeet, joita sovelluksesi tarvitsee. Liian sallivat määritykset voivat lisätä hyökkäysriskiä.
Yleinen esimerkki: CRM-järjestelmän tulisi paljastaa vain tarvittavat API-päätepisteet ja -otsakkeet valtuutetuille kolmannen osapuolen integraatioille, minimoiden hyökkäyspinta-alan.
5. Käytä HTTPS:ää turvalliseen viestintään
Käytä aina HTTPS:ää turvalliseen viestintään selaimen ja palvelimen välillä. HTTPS salaa selaimen ja palvelimen välillä siirretyt tiedot, estäen salakuuntelun ja väliintulohyökkäykset. HTTP:n käyttö voi altistaa arkaluontoiset tiedot hyökkääjille, vaikka CORS olisi määritetty oikein.
Yleinen esimerkki: Terveydenhuollon sovellusten on käytettävä HTTPS:ää suojatakseen potilastietoja, joita siirretään eri alkuperien välillä.
6. Sisällön tietoturvakäytäntö (Content Security Policy, CSP)
Vaikka se ei liity suoraan CORS:iin, sisällön tietoturvakäytäntö (CSP) on toinen tärkeä tietoturvamekanismi, joka voi auttaa estämään XSS-hyökkäyksiä. CSP:n avulla voit määritellä sallittujen lähteiden luettelon, joista selain saa ladata resursseja. Tämä voi auttaa estämään hyökkääjiä syöttämästä haitallisia komentosarjoja verkkosivustollesi, vaikka he onnistuisivat kiertämään muita turvatoimia.
Yleinen esimerkki: Rahoituslaitokset käyttävät usein tiukkoja CSP-käytäntöjä rajoittaakseen verkkopankkiportaaleihinsa ladatun sisällön lähteitä, mikä vähentää XSS-hyökkäysten riskiä.
Yleiset CORS-ongelmat ja vianmääritys
CORS-virheet voivat olla turhauttavia selvittää. Tässä on joitain yleisiä ongelmia ja niiden vianmääritysohjeita:
1. "No 'Access-Control-Allow-Origin' header is present on the requested resource."
Tämä on yleisin CORS-virhe. Se osoittaa, että palvelin ei palauta Access-Control-Allow-Origin-otsaketta vastauksessaan. Varmista, että palvelin on määritetty lähettämään oikeat CORS-otsakkeet pyynnön tekevän verkkosivun alkuperää varten. Tarkista palvelinpuolen koodi ja konfiguraatiotiedostot huolellisesti.
2. "Response to preflight request doesn't pass access control check: It does not have HTTP ok status."
Tämä virhe osoittaa, että esikysely epäonnistui. Tämä voi tapahtua, jos palvelin ei vastaa OPTIONS-pyyntöihin tai jos palvelin palauttaa virhetilakoodin (esim. 404, 500) vastauksena esikyselyyn. Varmista, että palvelimesi on määritetty käsittelemään OPTIONS-pyyntöjä ja että se palauttaa 200 OK -tilakoodin.
3. "Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'."
Tämä virhe ilmenee, kun yrität lähettää tunnistetietoja (esim. evästeitä) alkuperien välisessä pyynnössä ja Access-Control-Allow-Origin-otsake on asetettu arvoon *. Kuten aiemmin mainittiin, et voi käyttää jokerimerkkiä * lähettäessäsi tunnistetietoja. Sinun on määritettävä tarkka alkuperä, jolla on lupa käyttää resurssia.
4. Selaimen välimuisti
Selaimet voivat tallentaa CORS-vastauksia välimuistiin, mikä voi johtaa odottamattomaan käyttäytymiseen, jos CORS-konfiguraatio muuttuu. Välimuistiongelmien estämiseksi aseta vastauksen Cache-Control-otsakkeeksi no-cache, no-store tai max-age=0. Voit myös käyttää Access-Control-Max-Age-otsaketta hallitaksesi, kuinka kauan selain tallentaa esikyselyn tuloksia välimuistiin.
Vaihtoehtoja CORSille
Vaikka CORS on standarditapa mahdollistaa alkuperien väliset pyynnöt, on olemassa joitain vaihtoehtoja, joita voit harkita tietyissä tilanteissa:
1. JSON with Padding (JSONP)
JSONP on tekniikka, joka käyttää <script>-tagia kiertääkseen samaa alkuperää koskevan käytännön. JSONP toimii käärimällä JSON-tiedot JavaScript-funktiokutsuun. Selain suorittaa sitten JavaScript-funktion, välittäen JSON-tiedot argumenttina. JSONP on helpompi toteuttaa kuin CORS, mutta sillä on joitain rajoituksia. Se tukee vain GET-pyyntöjä, ja se on vähemmän turvallinen kuin CORS.
2. Käänteinen välityspalvelin
Käänteinen välityspalvelin (reverse proxy) on palvelin, joka sijaitsee API-palvelimesi edessä ja välittää pyynnöt sille. Käänteinen välityspalvelin voidaan määrittää lisäämään tarvittavat CORS-otsakkeet vastaukseen, piilottaen tehokkaasti alkuperien väliset pyynnöt selaimelta. Tämä lähestymistapa voi olla hyödyllinen, jos sinulla ei ole hallintaa API-palvelimesta tai jos haluat yksinkertaistaa CORS-konfiguraatiota.
Yhteenveto
Cross-Origin Resource Sharing (CORS) on ratkaisevan tärkeä tietoturvamekanismi, joka antaa verkkosivuille mahdollisuuden käyttää resursseja eri alkuperistä hallitusti. CORS:n toiminnan ymmärtäminen ja sen oikea toteuttaminen on olennaista turvallisten ja luotettavien verkkosovellusten rakentamisessa. Noudattamalla tässä artikkelissa esitettyjä parhaita käytäntöjä voit hallita CORS:ia tehokkaasti ja suojata API-rajapintojasi luvattomalta käytöltä.
Muista aina priorisoida turvallisuus CORS:ia konfiguroidessasi. Vältä jokerimerkkien käyttöä, vahvista Origin-otsake ja käytä HTTPS:ää turvalliseen viestintään. Näillä varotoimilla voit varmistaa, että verkkosovelluksesi ovat suojattuja sivustojenvälisiltä hyökkäyksiltä.
Tämä kattava opas tarjoaa vankan perustan CORS:n ymmärtämiseen. Viittaa aina oman palvelinteknologiasi viralliseen dokumentaatioon saadaksesi ajantasaisimmat tiedot ja parhaat käytännöt. Pysy ajan tasalla uusista tietoturvauhista ja mukauta CORS-konfiguraatiotasi vastaavasti.